콜백이란?

콜백이란 인자로 함수를 넘겨주고, 인자를 받은 함수가 해당 함수를 호출하는 것을 의미.

JS에서는 함수가 인자로 전달 될 수 있는 특성을 이용하여 콜백이 동작한다.

1
2
3
4
5
6
7
8
9
10
function calculator (func, num) {
// 로직이 끝나면 콜백 함수를 호출
return func(num);
}

function callbackFunc (num) {
return num + 1;
}

alert(calculator(callbackFunc, 1));
  • 비동기 처리를 위해서도 콜백이 사용된다.

JS는 본래 비동기적으로 실행이된다. 그래서, 작업이 끝나고 실행되야할 함수를 콜백함수로 넘김으로써, 작업이 끝나고 실행되도록 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
function f1(valueCallback) {
var result;
window.setTimeout(function(){
result = 10;
valueCallback(result);
}, 2000)
};

f1(function(value) {
console.log(value);
});

2 초 후에, f1 함수 인자로 넘긴, 함수가 실행되는 것을 확인할 수 있다.

Promise

위 콜백 함수를 좀더 효율적으로 작성할 수 있게 해주는 것이 Promise 이다. Promise는 ES6이상에서는 언어에 내장되어 있으며, 그 이전 버젼에서는 bluebirdQ.js 등의 라이브러리를 추가하여 사용할 수 있다.

Promise 구현체를 사용하여 명세에 따라 정의한 Promise 객체는 모두 then() 메소드를 가지고 있어서 Callback을 사용하지 않고도 절차적으로 동작하는 코드를 짤 수 있다.

1
2
3
4
5
6
7
8
var f1 = function() {
return new Promise(function(resolve, reject) {
window.setTimeout(function() { resolve(10); }, 2000);
});
};
f1().then(function(value) {
console.log(value);
});

bluebird

Promise의 구현체인 bluebird에 대해 알아보자

코어

  • new Promise()

Promise의 가장 기초가 되는 단위로, new Promise(function(resolve, reject) {...}) 의 형태를 가진다.

1
2
3
4
new Promise(function(resolve, reject){
if(error) reject(error);
else resolve(success);
});
  • then()

promise의 함수가 정상적으로 마무리를 했다면 resolve를 실행하는데, 그럼 .then()이 실행된다. 그 전까지 then의 실행은 보류된다.

위에서 실행했던 함수의 결과값을 매개변수로 갖을 수 있다.

1
2
3
4
5
6
7
8
9
10
new Promise(function(resolve, reject){
// ...
if(err) reject(err);
else resolve(users);
});
}).then(function(users){
res.send(users);
}).catch(function(err) {
console.error(err);
});
  • catch()

에러가 발생하여 reject가 수행되었을때, 실행된다. catch를 마지막에 연결하면 모든 에러처리를 한꺼번에 할 수도 있다.

  • Promise.all()

Promise들을 병렬로 처리할 수 있게 해준다. then의 매개변수값으로 배열이 들어온다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Promise.all([
new Promise(function(resolve, reject){
// ....
if(err) reject(err);
else resolve(result1);
},
new Promise(function(resolve, reject){
// ....
if(err) reject(err);
else resolve(result2);
}
]).then(function(result){
var result1 = result[0];
var result2 = result[1];
// ....
});
  • spread()

사용법은 then과 동일하지만 파라미터를 돌려주는 방식이 다르다.

1
2
3
4
5
6

Promise.all([
//위와 동일
]).spread(function(result1, result2) {
// ...
});
  • finally()

무조건 마지막에 실행된다. 에러가 있어도 무조건 실행이 된다.

  • bind()

then과 then 사이에서 변수를 공통으로 쓸 수 있게 만들어준다. .bind를 사용한 함수내에서 변수의 사용이 자유롭다.

1
2
3
4
5
6
7
8
somethingAsync().bind({}).spread(function (aValue, bValue) {
this.aValue = aValue;
this.bValue = bValue;
return somethingElseAsync(aValue, bValue);
}).then(function (cValue) {
// 여기서도 그대로 쓸 수 있다.
return this.aValue + this.bValue + cValue;
});
  • Promise.map()

인자로 literable한 인자를 넣으면, 반복되며 해당 함수를 호출할 수 있게 해준다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var promises = [];
for (var i = 0; i < fileNames.length; ++i) {
promises.push(fs.readFileAsync(fileNames[i]));
}
Promise.all(promises).then(function() {
console.log("done");
});

// Using Promise.map:
Promise.map(fileNames, function(fileName) {
// Promise.map awaits for returned promises as well.
return fs.readFileAsync(fileName);
}).then(function() {
console.log("done");
});

프로미스화

  • Promise.promisify()

인자로 받은 해당 함수를 promise wrapper로 감싸 리턴한다. 함수의 마지막 인자는 callback 함수이어야 한다.

readFile을 promise로 바꾸는 예제

1
2
3
4
5
6
7
8
9
var readFile = Promise.promisify(require("fs").readFile);

readFile("myfile.js", "utf8").then(function(contents) {
return eval(contents);
}).then(function(result) {
console.log("The result of evaluating myfile.js", result);
}).catch(function(e) {
console.log("Error reading file", e);
});
  • Promise.promisifyAll()

모듈 객체의 함수들을 모두, promise wrapper로 감싸 리턴한다. promisify의 복수형

1
2
3
4
5
6
7
var fs = Promise.promisifyAll(require("fs"));

fs.readFileAsync("myfile.js", "utf8").then(function(contents) {
console.log(contents);
}).catch(function(e) {
console.error(e.stack);
});

동기화 감시

  • .isFulfilled / .isRejected / .isPending / .isCancelled

Promise 안에서 로직을 실행하는 동안, 다음의 상태를 갖는다.

pending

아직 약속을 수행 중인 상태

fulfilled

약속이 지켜진 상태

rejected

약속이 못지켜진 상태

settled

약속이 지켜졌든 안지켜졌든 결론이 난 상태

상태 값을 확인 할 수 있다.

1
2
3
4
...
}).then(function(result) {
if(result.isFulfilled()) { }
})

Reference

https://www.inflearn.com/course/%EC%A7%80%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%96%B8%EC%96%B4-%EA%B8%B0%EB%B3%B8/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%BD%9C%EB%B0%B1%EA%B3%BC-ajax/

http://bluebirdjs.com/docs/api-reference.html

http://programmingsummaries.tistory.com/325

https://stackoverflow.com/questions/38340674/javascript-settimeout-callback

http://html5around.com/wordpress/tutorials/node-js%EC%97%90%EC%84%9C-bluebird-%EA%B8%B0%EC%B4%88-%EC%82%AC%EC%9A%A9%EB%B2%95-callback-%EC%A7%80%EC%98%A5%EC%97%90%EC%84%9C-%EB%B2%97%EC%96%B4%EB%82%98%EA%B8%B0/